﻿using Domain.IRepositorys;
using Domain.Model;
using Infrastructure.UniOfWorkBase;
using Microsoft.EntityFrameworkCore.Internal;
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using Infrastructure.Error;
using Domain.Types;
using Domain.Event;
using Domain.VO;
using Microsoft.EntityFrameworkCore;
using static Utility.PolicyUtil;
using Microsoft.Extensions.Logging;

namespace Domain.Services
{
    public class TaskService
    {
        public IEFUnitOfWork efUnitOfWork { get; set; }
        public IUserRepository userRepository { get; set; }
        public ITaskCategoryRepository taskCategoryRepository { get; set; }
        public IRecommendListRepository recommendListRepository { get; set; }
        public ITaskRepository taskRepository { get; set; }
        public ITaskLogRepository taskLogRepository { get; set; }
        public IWalletRepository walletRepository { get; set; }
        public IInComeWalletLogRepository inComeWalletLogRepository { get; set; }
        public IChargeWalletLogRepository chargeWalletLogRepository { get; set; }
        public ITaskTakeRepository taskTakeRepository { get; set; }

        public ILogger<TaskService> _logger { get; set; }

        public TaskCategoryAggregate GetCategory(int categoryID)
        {
            var c = taskCategoryRepository.GetByKey(categoryID);

            return c;
        }

        public IBlackListRepository blackListRepository { get; set; }
        public WalletService walletService { get; set; }


        #region 任务创建
        public void CheckUserCanCreate(UserInfoAggregate u)
        {
            var bl = blackListRepository.Entities.FirstOrDefault(i => i.UserID == u.ID && i.BlackListPermissions.HasFlag(BlackListPermissions.禁止发布) && !i.IsDelete);
            if (bl != null)
            {
                string rtime = bl.RestoreTime != null ? bl.RestoreTime.Value.AddDays(1).ToString("禁止发布任务到yyyy-MM-dd为止") : "禁止发布任务到永远";
                throw new BizException($"你被管理员列入黑名单列表中,{rtime}！");
            }
            if (!u.IsValidRealName())
            {
                throw new BizException("您未通过实名认证,请认证后重试！");
            }
            if (taskRepository.Entities.Count(i => i.UserID == u.ID && i.Status == Types.TaskStatus.进行中 && i.CT >= DateTime.Today) > u.GetTraderLevelConfig<int>("每天发布最大任务数"))
            {
                throw new BizException("您的发布任务最大数量已经超出限制,可升级为超级商人后在重试！");
            }
        }

        public void CheckTaskData(List<TaskFieldInfo> srcFieldData, bool NeedSubField, List<TaskFieldInfo> fieldData, List<TaskStepFieldInfo> stepFieldData, string subFieldName, int price, int totalCount, int totalPrice)
        {
            if (price < 120)
            {
                throw new BizException("投放单价不能低于1.2元！");
            }
            if (totalCount < 10)
            {
                throw new BizException("投放次数不能低于10次！");
            }
            if (totalPrice != (price * totalCount))
            {
                throw new BizException("投放总金额计算有误，请稍后在试！");
            }

            if (srcFieldData.Count > 0)
            {
                foreach (var item in fieldData)
                {
                    if (item.ID == Guid.Empty) throw new BizException("表单项ID不能为空！");

                    var f = srcFieldData.First(i => i.ID == item.ID);

                    if (f == null) throw new BizException("表单项未找到！");

                    if (f.IsRequired)
                    {
                        if (string.IsNullOrWhiteSpace(item.Text)) throw new BizException($"'{item.Name}'表单项为必填项！");

                        if (item.Text.Length < f.MinLength)
                        {
                            throw new BizException($"'{item.Name}'表单项为长度不能小于{f.MinLength}！");
                        }
                        if (item.Text.Length > f.MaxLength)
                        {
                            throw new BizException($"'{item.Name}'表单项为长度不能大于{f.MinLength}！");
                        }
                    }                    
                }
            }

            if (stepFieldData.Count == 0) throw new BizException($"'任务步骤'表单项为必填项！");

            if (!stepFieldData.Any(i => i.Type == TaskStepFieldType.图文说明)) throw new BizException($"至少有一个'{TaskStepFieldType.图文说明.ToString()}'！");
            if (!stepFieldData.Any(i => i.Type == TaskStepFieldType.收集截图)) throw new BizException($"至少有一个'{TaskStepFieldType.收集截图.ToString()}'！");

            foreach (var item in stepFieldData)
            {
                if (string.IsNullOrWhiteSpace(item.Text)) throw new BizException($"请输入{item.Type.ToString()}说明！");

                if (string.IsNullOrWhiteSpace(item.ImageUrl)) throw new BizException($"请上传{item.Type.ToString()}图片！");
            }

            if (NeedSubField)
            {
                if (string.IsNullOrWhiteSpace(subFieldName)) throw new BizException($"'提交数据'表单项为必填项！");
            }
        }

        

        public bool PayTask(int uid, int taskID, int taskAuditID)
        {
            var task = taskRepository.GetByKey(taskID);
            if (task == null) throw new BizException($"未找到任务！");
            if (task.UserID != uid) throw new BizException($"任务不是该用户创建！");
            var audit = task.TaskAudits.FirstOrDefault(i => i.ID == taskAuditID);
            if (audit == null) throw new BizException($"未找到需要支付的任务！");
            if (audit.IsPay) throw new BizException($"任务已经支付！");
            if (audit.AuditStatus == TaskAuditStatus.审核中) throw new BizException($"任务已经提交审核！");

            var chargeWalletLogAggregate = walletService.ChangeChargeWallet(uid, -audit.PayMoney, audit.PayMoney, taskAuditID.ToString(),
                                FinancialBizType.创建任务, task?.Name, "任务支付-任务审核ID");

            audit.IsPay = true;
            task.UT = DateTime.Now;
            task.FinishTime = DateTime.Now;
            //修改状态
            if (!audit.IsAdd)
            {
                audit.AuditStatus = TaskAuditStatus.审核中;
                var desc = $"总数量:{audit.Count} 待完成:{task.RemainCount} 已完成:{task.BeFinishCount} 总价格:{audit.TotalMoney} 支付记录 ChargeWalletLogAggregate";
                TaskLog(task.ID, uid, TaskLogType.创建任务, chargeWalletLogAggregate.Identity, desc, audit.ID);
            }
            else
            {
                audit.AuditStatus = TaskAuditStatus.审核通过;
                var desc = $"总数量:{audit.Count} 待完成:{task.RemainCount} 已完成:{task.BeFinishCount} 总价格:{audit.TotalMoney} 支付记录 ChargeWalletLogAggregate";
                TaskLog(task.ID, uid, TaskLogType.用户追加任务, chargeWalletLogAggregate.Identity, desc, audit.ID);

                task.FinishTime = audit.FinishTime;
                task.FreshTime = DateTime.Now;
                task.IsFinishAll = audit.IsFinishAll;
                task.LimitAudit = audit.LimitAudit;
                task.LimitTime = audit.LimitTime;
                task.RemainCount += audit.Count;
                task.TotalCount += audit.Count;
                task.UT = DateTime.Now;
                task.Status = TaskStatus.进行中;
            }
            taskRepository.Update(task);

            efUnitOfWork.Commit();

            return true;

        }

        #endregion

        #region 处理领取任务是否通过与系统释放
        public bool TaskFinishDo(int taskID)
        {
            var task = taskRepository.GetByKey(taskID);

            var taskStatusArr = new[] { TaskStatus.进行中, TaskStatus.任务待关闭, TaskStatus.暂停, TaskStatus.任务待完成 };
            if (!taskStatusArr.Contains(task.Status))
            {
                throw new BizException("任务当前状态不能执行任务完成操作！");
            }
            if (task.IsFinishAll && task.RemainCount > 0)
            {
                throw new BizException("任务当前没有完成不能执行任务完成操作！");
            }

            var statusArr = new[] { TaskTakeAuditStatus.待审核, TaskTakeAuditStatus.待提交 };

            if (taskTakeRepository.Entities.Any(i => i.TaskID == taskID && statusArr.Contains(i.Status)))
            {
                return false;
            }

            if (task.Status == TaskStatus.任务待关闭 || task.Status == TaskStatus.任务待完成)
            {
                goto Finish;
            }

            var totalDoenTask = taskTakeRepository.Entities.Count(i => i.TaskID == taskID && i.Status == TaskTakeAuditStatus.审核通过);

            if (task.BeFinishCount != totalDoenTask)
            {
                _logger.LogWarning($"TaskID:{taskID} task.BeFinishCount:{task.BeFinishCount} totalDoenTask:{totalDoenTask} 完成任务数量与任务数据数字不匹配");
                task.BeFinishCount = totalDoenTask;
            }
            //全部完成
            if (task.IsFinishAll)
            {
                if (task.BeFinishCount != task.TotalCount)
                {
                    _logger.LogWarning($"TaskID:{taskID} task.BeFinishCount:{task.BeFinishCount} totalDoenTask:{totalDoenTask} 完成任务数量与任务数据数字不匹配");
                    throw new BizException("任务结束总数量与剩余数量不匹配");
                }
            }
            else
            {
                if (task.FinishTime < DateTime.Now && (task.BeFinishCount + task.RemainCount) != task.TotalCount)
                {
                    _logger.LogWarning($"TaskID:{taskID} task.BeFinishCount:{task.BeFinishCount} totalDoenTask:{totalDoenTask} 完成任务数量与任务数据数字不匹配");
                    throw new BizException("任务结束总数量与剩余数量不匹配");
                }
                else if (task.FinishTime > DateTime.Now && task.BeFinishCount != task.TotalCount)
                {
                    _logger.LogWarning($"TaskID:{taskID} task.BeFinishCount:{task.BeFinishCount} totalDoenTask:{totalDoenTask} 完成任务数量与任务数据数字不匹配");
                    throw new BizException("任务结束总数量与剩余数量不匹配");
                }
            }


            Finish:
            var desc = $"总数量:{task.TotalCount} 剩余数量:{task.RemainCount} 已完成:{task.BeFinishCount} ";
            TaskLog(task.ID, 0, TaskLogType.关闭任务, null, desc);




            if (task.Status == TaskStatus.任务待关闭)
            {
                var returnFee = task.RemainCount * task.Price;

                task.Status = Domain.Types.TaskStatus.任务关闭;
                task.TotalCount = task.BeFinishCount;
                task.RemainCount = 0;

                walletService.ChangeChargeWallet(task.UserID, returnFee, -returnFee, task.ID.ToString(), FinancialBizType.任务关闭
                    , "任务关闭", $"系统关闭任务退回{((double)returnFee) / 100}金额 {desc}");
            }
            else
            {
                if (task.RemainCount != 0) throw new BizException("任务待完成状态有未完成的数量");
                task.Status = Domain.Types.TaskStatus.任务完成;
                task.TotalCount = task.BeFinishCount;
                task.RemainCount = 0;
            }



            efUnitOfWork.Commit();

            return true;
        }

        public void DoTaskClose(TaskAggregate task, TaskStatus taskStatus)
        {
            switch (taskStatus)
            {
                case TaskStatus.任务待完成:
                case TaskStatus.任务完成:
                    if (task.Status == TaskStatus.任务待关闭)
                    {
                        if (task.TotalCount == (task.BeFinishCount + task.RemainCount))
                        {
                            task.AddDomainEvent(new CloseTaskEvent()
                            {
                                TaskID = task.ID
                            });
                        }
                    }
                    else
                    {
                        if (task.TotalCount == task.BeFinishCount)
                        {
                            task.Status = TaskStatus.任务待完成;
                            task.AddDomainEvent(new CloseTaskEvent()
                            {
                                TaskID = task.ID
                            });
                        }

                        if (!task.IsFinishAll && task.FinishTime < DateTime.Now && (task.TotalCount == (task.BeFinishCount + task.RemainCount)))
                        {
                            task.Status = TaskStatus.任务待完成;
                            task.AddDomainEvent(new CloseTaskEvent()
                            {
                                TaskID = task.ID
                            });
                        }
                    }

                    break;
                case TaskStatus.任务待关闭:
                case TaskStatus.任务关闭:
                    task.Status = TaskStatus.任务待关闭;

                    if (task.TotalCount == (task.BeFinishCount + task.RemainCount))
                    {
                        task.AddDomainEvent(new CloseTaskEvent()
                        {
                            TaskID = task.ID
                        });
                    }
                    break;
            }
        }



        public bool TakeTaskAuditPass(int uid, int taskTakeID)
        {
            var taketask = taskTakeRepository.GetByKey(taskTakeID);
            var task = taskRepository.GetByKey(taketask.TaskID);

            if (task.UserID != uid) throw new BizException("任务不是该用户的无法审核！");

            taketask.Status = TaskTakeAuditStatus.审核通过;
            taketask.StatusInfo = "审核通过";
            taketask.AuditFinishTime = DateTime.Now;

            taketask.AddDomainEvent(new FinishTakeTaskEvent()
            {
                TaskTakeID = taskTakeID,
                AuditUserID = uid
            });
            task.BeFinishCount++;

            var desc = $"总数量:{task.TotalCount} 剩余数量:{task.RemainCount} 已完成:{task.BeFinishCount} 支付金额:{taketask.Price} 领取任务Identity";
            TaskLog(task.ID, uid, TaskLogType.领取任务审核通过, taketask.Identity, desc);

            DoTaskClose(task, TaskStatus.任务完成);

            try
            {
                efUnitOfWork.Commit();
            }
            catch (DbUpdateConcurrencyException ex)
            {
                efUnitOfWork.Rollback();
                throw new PolicyRetryException();
            }

            return true;
        }

        public void ReleaseTake(TaskTakeAggregate item)
        {
            var task = taskRepository.GetByKey(item.TaskID);

            try
            {
                task.RemainCount++;

                item.Status = TaskTakeAuditStatus.审核拒绝;
                item.StatusInfo = "用户超时未提交任务自动关闭";
                item.UT = DateTime.Now;

                var desc = $"总数量:{task.TotalCount} 剩余数量:{task.RemainCount} 已完成:{task.BeFinishCount} 用户超时未提交";
                TaskLog(task.ID, item.UserID, TaskLogType.用户超时释放, item.Identity, desc);

                DoTaskClose(task, TaskStatus.任务完成);

                efUnitOfWork.Commit();
            }
            catch (DbUpdateConcurrencyException ex)
            {
                efUnitOfWork.Rollback();
                throw new PolicyRetryException();
            }
        }
        public bool TakeTaskAuditNoPass(int uid, int taskTakeID, string auditInfo)
        {
            var taketask = taskTakeRepository.GetByKey(taskTakeID);
            var task = taskRepository.GetByKey(taketask.TaskID);

            if (task.UserID != uid) throw new BizException("任务不是该用户的无法审核！");

            taketask.Status = TaskTakeAuditStatus.审核拒绝;
            taketask.StatusInfo = auditInfo;

            task.RemainCount++;

            var desc = $"总数量:{task.TotalCount} 剩余数量:{task.RemainCount} 已完成:{task.BeFinishCount} 领取任务Identity";
            TaskLog(task.ID, uid, TaskLogType.领取任务审核未通过, taketask.Identity, desc);

            DoTaskClose(task, TaskStatus.任务完成);

            try
            {
                efUnitOfWork.Commit();
            }
            catch (DbUpdateConcurrencyException ex)
            {
                efUnitOfWork.Rollback();
                throw new PolicyRetryException();
            }

            return true;
        }

        #endregion

        #region 领取任务
        public bool CheckUserCanTakeTask(int uid, int taskID)
        {
            var u = userRepository.GetByKey(uid);

            var task = taskRepository.GetByKey(taskID);
            var arr = new[] { TaskTakeAuditStatus.审核拒绝, TaskTakeAuditStatus.审核通过 };
            if (taskTakeRepository.Entities.Any(i => i.UserID == uid && i.TaskID == taskID && !arr.Contains(i.Status)))
            {
                throw new BizException($"当前任务未完成，无法重复领取！");
            }

            if (task == null) throw new BizException($"未找到任务！");

            var bl = blackListRepository.Entities.FirstOrDefault(i => i.UserID == u.ID && i.BlackListPermissions.HasFlag(BlackListPermissions.禁止领任务) && !i.IsDelete);
            if (bl != null)
            {
                string rtime = bl.RestoreTime != null ? bl.RestoreTime.Value.AddDays(1).ToString("禁止领任务到yyyy-MM-dd为止") : "禁止领任务到永远";
                throw new BizException($"你被管理员列入黑名单列表中,{rtime}！");
            }

            if (task.UserID == u.ID) throw new BizException($"不能领取自己的任务！");

            if (task.Status != TaskStatus.进行中) throw new BizException($"任务状态不能被领取！");

            if ((!task.IsFinishAll) && task.FinishTime > DateTime.Now)
            {
                //task.Status = TaskStatus.任务待完成;
                DoTaskClose(task, TaskStatus.任务完成);
                efUnitOfWork.Commit();

                throw new BizException($"当前领取的任务，已经超过设置的完成时间！");
            }

            if (task.RemainCount <= 0) throw new BizException($"任务已被领完！");

            if (taskTakeRepository.Entities.Count(i => i.UserID == u.ID && i.Status == TaskTakeAuditStatus.待提交) > u.GetTraderLevelConfig<int>("用户领取最大任务数")) throw new BizException($"用户领取最大任务数为{u.GetTraderLevelConfig<int>("用户最大任务领取数")}个！");


            return true;
        }

        public TaskTakeAggregate TakeTask(int uid, int taskID)
        {
            var u = userRepository.GetByKey(uid);

            var task = taskRepository.GetByKey(taskID);

            task.RemainCount--;

            TaskTakeAggregate taskTakeAggregate = new TaskTakeAggregate();

            taskTakeAggregate.OutTime = DateTime.Now.AddHours((int)task.LimitTime);
            taskTakeAggregate.Status = TaskTakeAuditStatus.待提交;
            taskTakeAggregate.UserID = u.ID;
            //taskTakeAggregate.TaskSrcData = TaskSrcDataVO.Create(task);
            taskTakeAggregate.FieldData = task.FieldData;
            taskTakeAggregate.Price = task.Price;
            taskTakeAggregate.StepFieldData = task.StepFieldData;
            taskTakeAggregate.SubFieldName = task.SubFieldName;
            taskTakeAggregate.TakeLimitAudit = task.LimitAudit;
            taskTakeAggregate.TakeLimitTime = task.LimitTime;
            taskTakeAggregate.TaskID = task.ID;
            taskTakeAggregate.TaskUserID = task.UserID;

            double uTaskPer = u.GetTaskCommissionPer();

            //用户收益
            int uFee = (int)(taskTakeAggregate.Price * uTaskPer);
            taskTakeAggregate.PreFee = uFee;



            taskTakeAggregate.AddDomainEvent(new TakeTaskEvent()
            {
                UserID = u.ID,
                TaskID = task.ID,
                TakeTaskIdentity = taskTakeAggregate.Identity
            });
            taskTakeAggregate = taskTakeRepository.Insert(taskTakeAggregate);

            var desc = $"总数量:{task.TotalCount} 剩余数量:{task.RemainCount} 已完成:{task.BeFinishCount} 领取任务Identity";
            TaskLog(task.ID, u.ID, TaskLogType.用户领取任务, taskTakeAggregate.Identity, desc);

            try
            {
                efUnitOfWork.Commit();
            }
            catch (DbUpdateConcurrencyException ex)
            {
                efUnitOfWork.Rollback();
                throw new PolicyRetryException();
            }

            return taskTakeAggregate;
        }
        #endregion

        public bool AuditTask(int adminid, int taskID, int taskAuditID, TaskAuditStatus taskAuditStatus, string auditReson)
        {
            var task = taskRepository.GetByKey(taskID);
            if (task == null) throw new BizException($"未找到任务！");

            var audit = task.TaskAudits.FirstOrDefault(i => i.ID == taskAuditID);
            if (audit == null) throw new BizException($"未找到需要支付的任务！");

            if (!audit.IsPay) throw new BizException($"任务未支付！");
            if (audit.AuditStatus != TaskAuditStatus.审核中) throw new BizException($"任务未提交审核！");

            if (taskAuditStatus == TaskAuditStatus.审核通过)
            {
                audit.AuditStatus = taskAuditStatus;
                audit.UT = DateTime.Now;
                audit.AuditInfo = "审核通过";
                audit.AdminID = adminid;
                task.FinishTime = audit.FinishTime;
                task.FreshTime = DateTime.Now;
                task.IsFinishAll = audit.IsFinishAll;
                task.LimitAudit = audit.LimitAudit;
                task.LimitTime = audit.LimitTime;
                task.RemainCount += audit.Count;
                task.TotalCount += audit.Count;
                task.UT = DateTime.Now;
                task.Status = TaskStatus.进行中;

                taskRepository.Update(task);

                var desc = $"总数量:{task.TotalCount} 剩余数量:{task.RemainCount} 已完成:{task.BeFinishCount}";
                TaskLog(task.ID, adminid, TaskLogType.任务审核通过, null, desc, audit.ID);

                efUnitOfWork.Commit();

                return true;
            }
            else
            {

                var chargeWalletLogAggregate = walletService.ChangeChargeWallet(adminid, audit.PayMoney, -audit.PayMoney, taskAuditID.ToString(),
                                        FinancialBizType.任务审核失败回退, task?.Name, "任务审核失败回退-任务审核ID");

                audit.AuditStatus = TaskAuditStatus.审核未通过;
                audit.UT = DateTime.Now;
                audit.AuditInfo = auditReson;

                task.UT = DateTime.Now;
                taskRepository.Update(task);


                var desc = $"总数量:{task.TotalCount} 剩余数量:{task.RemainCount} 已完成:{task.BeFinishCount} 原因:{auditReson}  退款记录 ChargeWalletLogAggregate";
                TaskLog(task.ID, adminid, TaskLogType.任务审核未通过, chargeWalletLogAggregate.Identity, desc, audit.ID);


                efUnitOfWork.Commit();

                return true;
            }
        }

        public void Restore(int taskID)
        {
            var task = taskRepository.GetByKey(taskID);
            task.Status = TaskStatus.进行中;
            var desc = $"总数量:{task.TotalCount} 剩余数量:{task.RemainCount} 已完成:{task.BeFinishCount} 原因:恢复任务";
            TaskLog(task.ID, 0, TaskLogType.任务暂停恢复, null, desc);

        }

        public int GetPayTaskChargeFee(int uid, TaskAggregate task, int taskAuditID)
        {
            var audit = task.TaskAudits.FirstOrDefault(i => i.ID == taskAuditID);
            if (audit == null) throw new BizException($"未找到待支付任务！");
            var wallet = walletRepository.Entities.FirstOrDefault(i => i.UserID == uid);

            var chazhi = audit.PayMoney - wallet.ChargeBlance;

            return chazhi > 0 ? chazhi : 0;
        }

        public bool FreshTask(int uid, int taskID)
        {
            var task = taskRepository.GetByKey(taskID);

            task.FreshTime = DateTime.Now;

            var desc = "刷新任务时间";
            TaskLog(task.ID, uid, TaskLogType.刷新任务时间, null, desc);

            return true;
        }

        public void TaskLog(int TaskID, int OperatorID, TaskLogType taskLogType, Guid? BizID, string desc, int? auditID = null)
        {
            TaskLogAggregate taskLogAggregate = new TaskLogAggregate();

            taskLogAggregate.TaskID = TaskID;
            taskLogAggregate.TaskAuditID = auditID;
            taskLogAggregate.OperatorID = OperatorID;
            taskLogAggregate.Description = desc;
            taskLogAggregate.TaskType = taskLogType;
            taskLogAggregate.TaskTypeName = taskLogAggregate.TaskType.ToString();
            taskLogAggregate.BizID = BizID;

            taskLogRepository.Insert(taskLogAggregate);
        }

        public bool HasRecommend(int taskID)
        {
            return recommendListRepository.Entities.Any(i => i.TaskID == taskID && i.EndTime > DateTime.Now);
        }

        public void PutRecommendListForHour(int uid, int taskID, int hour, bool isPay, string Remark)
        {
            RecommendListAggregate recommendListAggregate = new RecommendListAggregate();

            recommendListAggregate.CT = recommendListAggregate.UT = DateTime.Now;
            recommendListAggregate.EndTime = DateTime.Now.AddHours(hour).AddMinutes(1);
            recommendListAggregate.TaskID = taskID;
            recommendListAggregate.UserID = uid;
            recommendListAggregate.IsPay = isPay;
            recommendListAggregate.Remark = Remark;
            recommendListAggregate.SetTime = DateTime.Now;

            recommendListRepository.Insert(recommendListAggregate);
        }
    }
}
